﻿/**
 * ...
 * @author Krzysztof Rozalski
 */

package hapi.flash.util;
import flash.display.Loader;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import hapi.event.Notifier;
import hapi.event.HapiDispatcher;
import haxe.Timer;

class LoaderQueue {

	var urls:Array<String>;
	var loaders:List<Loader>;
	var onComplete:Void->Void;
	var onProgress:Float->Void;
	var toLoad:Int;
	var bytesLoaded:Float;
	var bytesTotal:Float;
	var timer:Timer;
	var loadAllAtOnce:Bool;
	var domain:ApplicationDomain;
	
	var errorDispatcher:HapiDispatcher<LoaderErrorEvent>;
	public var onError(getOnError, null):Notifier<LoaderErrorEvent>;
	
	static var IO_ERRORS = [IOErrorEvent.IO_ERROR, IOErrorEvent.DISK_ERROR, IOErrorEvent.DISK_ERROR, IOErrorEvent.NETWORK_ERROR, IOErrorEvent.VERIFY_ERROR];
	
	public function new(?loadAllAtOnce = true, ?domain:ApplicationDomain) {
		this.domain = domain == null ? ApplicationDomain.currentDomain : domain;
		this.loadAllAtOnce = loadAllAtOnce;
		urls = new Array();
		errorDispatcher = new HapiDispatcher<LoaderErrorEvent>();
	}
	
	public function start( onComplete:Void->Void, ?onProgress:Float->Void ):Void {
		loaders = new List<Loader>();
		toLoad = urls.length;
		this.onComplete = onComplete;
		this.onProgress = onProgress;
		bytesLoaded = 0.;
		bytesTotal = 0.;
		startLoadingAll();
	}
	
	public function add(url:String) {
		urls.push(url);
	}
	
	function startLoadingAll() {
		if(loadAllAtOnce)
			Lambda.iter(urls, loadFromUrl );
		else
			loadFromUrl(urls[0]);
		timer = new Timer(20);
		timer.run = refreshProgress;
	}
	
	function loadFromUrl(url) {
		var loader = new Loader();
		var addLoaderEvent = loader.contentLoaderInfo.addEventListener;
		addLoaderEvent( Event.COMPLETE, createCompleteListenerFor(url) );
		
		for( e in IO_ERRORS )
			addLoaderEvent( IOErrorEvent.IO_ERROR, createErrorListenerFor(url, e, loader) );
			
		loader.load( new URLRequest(url), new LoaderContext(false, domain));
		loaders.add(loader);
	}
	
	function createCompleteListenerFor(url) {
		var me = this;
		return function(event:Event) {
			trace("completed " + url);
			me.setAsLoaded(url);
		}
	}
	
	function createErrorListenerFor(url, errorType, loader) {
		var me = this;
		return function(event:Event) {
			me.errorDispatcher.dispatchEvent( {
				event:event,
				url:url,
				type:errorType,
				source:loader
			});
		}
	}
	
	function setAsLoaded(url) {
		toLoad--;
		if ( toLoad <= 0 ) {
			timer.stop();
			onComplete();
		}else if(!loadAllAtOnce){
			loadFromUrl(urls[urls.length - toLoad]);
		}
	}
	
	function refreshProgress() {
		var loaded:Float = 0;
		var total:Float = 0;
		for ( l in loaders ) {
			loaded += l.contentLoaderInfo.bytesLoaded;
			total += l.contentLoaderInfo.bytesTotal;
		}
		if ( bytesLoaded !=  loaded || bytesTotal != total ) {
			bytesLoaded = loaded;
			bytesTotal = total;
			if(bytesTotal > 1024 )
				onProgress(bytesLoaded / bytesTotal);
		}
	}
	
	function getOnError():Notifier<LoaderErrorEvent> {
		return errorDispatcher;
	}
}